-------------------------------------------------------------------------- In This Issue: BFLI - New graphics modes 2 FLI gave us more color to the screen, AFLI increased the horizontal resolution and partly the color selection by using the hires mode. BFLI stands for 'Big FLI' and gives us 400 lines instead of the usual two hundred. AFLI and BFLI can be combined, but we are not going into that (yet ?). Pasi 'Albert' Ojala albert@cs.tut.fi -------------------------------------------------------------------------- BFLI - New graphics modes 2 --------------------------- One day I was watching some demos that used linecrunch routines for whole-screen multicolor-graphics upscrollers. I already had my theories about how and why linecrunch worked, but because I had not used it anywhere, the details were a bit vague. In fact, I have many times accidentally created linecrunch effects when trying to do something else with $D011. Probably every demo coder has. But you learn by doing. I had the idea of using linecrunch for FLI instead of a simple multicolor picture as it always seemed to be used. However, this has probably been done before and because I don't like to do things that have been done before, I decided to use linecrunch to show a two-screen-tall FLI picture. _Linecrunch Basics_ For those not familiar with linecrunch routines: linecrunch is used to scroll the screen UPWARDS by convincing VIC-II that it has already showed more character rows than it in reality has shown. Surprisingly (or then, maybe not :) this consists of fiddling with $D011. The timing is critical as always. Linecrunch works by setting $D011 equal the line before the current line and VIC-II will happily think that it is time to move on to the next character row - add 40 to the video matrix counter, 320 to the graphics memory counter and be ready to start a bad line. Or, maybe 'NOT to go back to the current row' would be a more suitable description. (Programming VIC-II is slowly becoming a science.) The required timing also does not cause bad lines so that you can skip another character row immediately on the successive line. Because linecrunch causes VIC-II to skip rows, it will run out of video matrix and color memory (and graphics memory) to show before reaching the end of the screen. However, VIC-II does not stop displaying the graphics nor does it reset the internal counters. The counters keep on running and wrap around instead. Normally, when VIC-II is displaying the last character row, it is showing the memory from offsets $3c0 to $3e7. If VIC-II has skipped one character row, it is displaying from $3e8 to $40f instead. But, there are only 10 bits in the video matrix counter (locations 0..1023), so it wraps around to zero after $3ff. This means that the beginning of the video matrix is displayed at the bottom of the screen. The character rows become shifted by 24 character positions to the right because there were originally 24 unused memory locations at the end of the memory (1000..1023). (To be honest, sprite image pointers are not unused memory, but they are not used with normal FLI.) ____________________ ____________________ |abcdefghijklmnopqrst| |abcdefghijklmnopqrst| | | |--------------------| <- Skipped row : : : : : : : : : : : : | | |normally last line | |normally last line | |XXXXXXXXZZZZabcdefgh| `--------------------' `--------------------' X = unused mem (1000..1015) Z = sprite pointers (1016..1023) Figure 1: Linecrunch The same thing happens for color memory because it uses the same counter for addressing the memory (in fact, color memory access and character data access are performed simultaneosly, 12 bits at a time). The graphics memory behaves the same way, except that the counter has three bits more and it counts at eight times the speed, so that it wraps at the exact same time as the other counter. _Back to BFLI_ Wrapped data is nothing difficult to work with. It is just the matter of writing the right conversion program. (Which still may be more than you can handle on the first attempt. I made several attempts to get the file format right in the first place. And the format has changed twice after that.) Also, the normal FLI routine can be used, we only have to make sure VIC always has the right bank visible - simple LDA bank,x:sta $DD00 can accomplish that. The more difficult aspect is to make the display freely locatable. We have 33 kilobytes of graphics data, this is the main reason we can't even think about using copying. Linecrunch combined with the bad line delaying technique will do the job much more nicely. Figure 2 shows the principles. To make things simpler I have chosen location 0 to mean that the top of the picture is visible, 1 means that the picture is scrolled one line upwards and so on. We can see that linecrunch is not used at all for the location 0. To make the picture start at the same point whether linecrunch has crunched lines or not we compensate the non-lost raster lines by delaying the next bad line. When the location is n*8 (n=0,1,2..), the sum of the linecrunched and delayed lines is constant - the graphics display always starts at the same point. Then how do we deal with the location values that are not evenly dividable by eight ? Now, lets assume that the location is L, and we have C, which is the location divided by eight (C = L/8), and R, which is the remainder (R = L%8). To make the picture scroll to the right position, we need to delay the bad line less than before - R lines less for location L than for location C*8. E.g. for location 2 we delay the bad line two lines less than for location 0. This also shows that we need 7 lines more than is needed for to compensate for the linecrunch. Determining the number of linecrunch lines is a recursive process, because when you use more linecrunch lines, that decreases the number of lines you have available for the display and you need bigger range for the location value. We lose about two lines when we use the soft y-scroll with the linecrunch and the 7 lines used in the soft y-scroll itself. This makes 191 lines available for the display originally. Because we need to show 400 lines of graphics, we would need (400-191)/8=27 linecrunch lines. However, this in turn reduces the number of lines we have for graphics to 191-27=164 and we need (400-164)/8=30 linecrunch lines. Again, 191-30 is 161. We get (400-161)/8=30 and there it finally converges and we have 161 lines for graphics, which makes location values 0..238 valid. Location 0 1 2 .. 8 9 .. 238 ___________________.. ___________.. ________ Linecrunch -------------------.. ___________.. ^ ^ ^ | | | ^ ^ | | | | | Bad line delayed| | | | | | | | | | ======== | | v | | 232 | v ___.. | v : v ________0 v ___.. : Gfx Enabled ________0_______1__.. ________8__.. 237_____ 0 1 2 8 9 238 1 2 3 9 10 239 2 3 4 10 11 240 3 4 5 11 12 241 4 5 6 12 13 242 5 6 7 13 14 243 6 7 8 14 15 244 7 8 9 15 16 245 : : : : : : : : : : : : 160 161 162.. 168 169.. 399 Figure 2: Linecrunch and DMA delay in BFLI (Graphics lines not in scale) _Clipping added_ Now we can scroll the picture to any location we want, but the top of the picture is not clipped and it is very annoying to watch. (Change AND #$77 to AND #$37 inside LOOP 0 to see what I mean.) We need to enable the graphics at the same point regardless of the y-scroll value. The answer is in the extended color mode (ECM). When both ECM and multicolor mode (MCM) are selected, VIC-II will turn the display to black. This is because there is a conflicting situation and it just can't decide which color scheme to use. The video accesses will continue to happen just like before, the data is just not displayed. When the ECM bit is cleared again, the normal multicolor graphics is again shown. So, we set the ECM bit and start to display the first eight lines of the FLI. Because the FLI routine already writes to $D011, we just make sure the ECM bit is set in the first R number of writes to $D011 and zero in all other. Because the raster line that FLI starts at changes, we would have to update the values we stuff into $d011 anyway. The same values with ECM mode bit cleared are then used until the end of the display. The viewer is now 'complete'. You can take a look at the code below or you can get C64Gfx1_4.lha and see it in action yourself and not just rely on my word. The package includes converter programs for BFLI, FLI and Koala (ANSI-C), couple of example pictures and viewers for PAL and NTSC machines. -Pasi 'Albert' Ojala albert@cs.tut.fi -------------------------------------------------------------------------- BFLI viewer program for PAL machines UPOS = $C00 ; temporary area for tables BANK = $D00 ; UPOS for linecrunch, BANK for FLI bank select RASTER= 27 ; where to position the sprite -> IRQ 20 lines later DUMMY = $FFF ; dummy location for timing purposes FLISZ = 21-1 ; visible FLI size in character rows - 1 *= $810 SEI LDA #$7F:STA $DC0D ; IRQ setup LDA #1:STA $D01A STA $D015:STA KEYW+1 LDA #IRQ:STA $315 LDA #RASTER:STA $D001 ; I know, using a sprite to synch is CLC:ADC #20:STA $D012 ; only the shortest, not the best way:-) LDA #0:STA $D017 JSR NEWPOS ; Init the FLI routines LDA #$A:STA $D011 ; Blank screen LDX #23 ; Init tables BLOOP LDA #$94:STA BANK,X LDA #$96:STA BANK+24,X DEX:BPL BLOOP LDX #15 LOOP0 LDA YINIT,X:AND #$77 ; Change to $37 to better see the STA UPOS,X ; workings of the routines STA UPOS+16,X STA UPOS+32,X DEX:BPL LOOP0 LDA #$34:STA 1 ; Copy to the last video bank LDA #$80:STA SRC+2 ; from $8000-$BFFF to $C000-$FFFF LDA #$C0:STA DST+2 LDX #0:LDY #$40 SRC LDA $8000,X DST STA $C000,X INX:BNE SRC INC SRC+2:INC DST+2 DEY:BNE SRC LDA #$37:STA 1 LDX #0 ; Init color memory LP LDA $3C00,X:STA $D800,X ; All 1024 bytes are used LDA $3D00,X:STA $D900,X ; - some even twice! LDA $3E00,X:STA $DA00,X LDA $3F00,X:STA $DB00,X INX:BNE LP LDA $DC0D:CLI KEYW LDX #0:BNE KEYW ; Wait for space to be pressed SEI ; System to normal LDA #$37:STA 1 JSR $FDA3 LDA #$97:STA $DD00 JSR $E5A0 LDY #3 IRQL LDA $FD30,Y:STA $314,Y DEY:BPL IRQL LDX #0:LDA #1 ; Clear color memory CLL STA $D800,X:STA $D900,X STA $DA00,X:STA $DB00,X INX:BNE CLL CLI:RTS YINIT BYT $78,$79,$7A,$7B,$7C,$7D,$7E,$7F BYT $78,$79,$7A,$7B,$7C,$7D,$7E,$7F ; Align to page boundary so that the loops work ; even if we added routines above.. *=*-<*+256 IRQ LDA #$18:STA $D016 LDX #0:LDA #$5F INC DUMMY:DEC DUMMY ; Synchronization STX $D020:STX $D021:STA $D011 LDA #$15:STA $D018 LDX #0:NOP:NOP:BIT $EA E1 CPX #0:BEQ E2 NOP:NOP:NOP:NOP:NOP:NOP:NOP LDA UPOS+0,X:INC DUMMY:STA $D011 ; Linecrunch-part NOP:NOP:INC DUMMY:NOP:NOP:NOP NOP:NOP:NOP:NOP:INX:CLC:BCC E1 E2 CPX #29:BEQ EJUMP NOP:NOP:NOP:BIT $EA LDA UPOS+4,X:INC DUMMY:STA $D011 ; Delay DMA to compensate NOP:NOP:NOP:INC DUMMY:NOP:NOP NOP:NOP:NOP:NOP:NOP:BIT $EA:INX:CLC:BCC E2 EJUMP BIT $EA ; Now wait for the right time and start FLI B0 LDA #$92:STA $DD00 ; The right video bank for the first row ; Wait for 0-7 lines to set the ECM mode off ; (makes the graphics visible) F0 LDA #0:STA $D011:LDA #$08:STA $D018:NOP:NOP:NOP:NOP:BIT $EA F1 LDA #0:STA $D011:LDA #$18:STA $D018:NOP:NOP:NOP:NOP:BIT $EA F2 LDA #0:STA $D011:LDA #$28:STA $D018:NOP:NOP:NOP:NOP:BIT $EA F3 LDA #0:STA $D011:LDA #$38:STA $D018:NOP:NOP:NOP:NOP:BIT $EA F4 LDA #0:STA $D011:LDA #$48:STA $D018:NOP:NOP:NOP:NOP:BIT $EA F5 LDA #0:STA $D011:LDA #$58:STA $D018:NOP:NOP:NOP:NOP:BIT $EA F6 LDA #0:STA $D011:LDA #$68:STA $D018:NOP:NOP:NOP:NOP:BIT $EA F7 LDA #0:STA $D011:LDA #$78:STA $D018:LDX #FLISZ:NOP:NOP:NOP:BIT $EA ; Do FLI 19 more character rows F8 LDA #0:STA $D011:LDA #$08:STA $D018 B1 LDA BANK,X:STA $DD00:BIT $EA F9 LDA #0:STA $D011:LDA #$18:STA $D018:NOP:NOP:NOP:NOP:BIT $EA FA LDA #0:STA $D011:LDA #$28:STA $D018:NOP:NOP:NOP:NOP:BIT $EA FB LDA #0:STA $D011:LDA #$38:STA $D018:NOP:NOP:NOP:NOP:BIT $EA FC LDA #0:STA $D011:LDA #$48:STA $D018:NOP:NOP:NOP:NOP:BIT $EA FD LDA #0:STA $D011:LDA #$58:STA $D018:NOP:NOP:NOP:NOP:BIT $EA FE LDA #0:STA $D011:LDA #$68:STA $D018:NOP:NOP:NOP:NOP:BIT $EA FF LDA #0:STA $D011:LDA #$78:STA $D018:NOP:NOP:DEX:BMI EFLI:JMP F8 EFLI NOP:LDA #$FD WL CMP $D012:BNE WL NOP:NOP:LDA #$C:INC DUMMY:INC DUMMY:INC DUMMY INC DUMMY:INC DUMMY:INC DUMMY:INC DUMMY:INC DUMMY:INC DUMMY:STA $D020 JSR CHPOS ; Change to a new location JSR NEWPOS ; Update the routine to correspond to the location LDA $DC01:AND #$10:BNE OV3 ; Check for the space bar LDA #0:STA KEYW+1 OV3 LDX #$53:STX $D011:INC $D019:JMP $EA81 NEWPOS LDA #0 ; Init the IRQ routine for this position LSR:LSR:LSR:STA E1+1 LDA #7:SEC:SBC NEWPOS+1:AND #7:TAX:TAY:CLC:ADC #29:STA E2+1 ; Update $d011 values for FLI ; X has the number of lines (0..7) that should have ECM mode set LDA UPOS+5+7,Y:DEX:BMI J0:AND #$3F J0 STA F7+1:AND #$3F:STA FF+1 LDA UPOS+5+6,Y:DEX:BMI J1:AND #$3F J1 STA F6+1:AND #$3F:STA FE+1 LDA UPOS+5+5,Y:DEX:BMI J2:AND #$3F J2 STA F5+1:AND #$3F:STA FD+1 LDA UPOS+5+4,Y:DEX:BMI J3:AND #$3F J3 STA F4+1:AND #$3F:STA FC+1 LDA UPOS+5+3,Y:DEX:BMI J4:AND #$3F J4 STA F3+1:AND #$3F:STA FB+1 LDA UPOS+5+2,Y:DEX:BMI J5:AND #$3F J5 STA F2+1:AND #$3F:STA FA+1 LDA UPOS+5+1,Y:DEX:BMI J6:AND #$3F J6 STA F1+1:AND #$3F:STA F9+1 LDA UPOS+5+0,Y:DEX:BMI J7:AND #$3F J7 STA F0+1:AND #$3F:STA F8+1 LDA #$96:STA B0+1:LDA #$C7:SEC:SBC NEWPOS+1:BCC OV2:LSR:LSR:LSR CLC:ADC #3:STA B1+1:RTS OV2 LDA #0:STA B1+1:LDX #$94:STX B0+1:RTS CHPOS LDX NEWPOS+1 ; Get old position LDA $DC00:TAY ; Get joystick AND #$10:BNE DIR ; If no button pressed TYA:AND #1:BEQ UP ; If joy up TYA:AND #2:BEQ DOWN ; If joy down RTS DIR LDA #0:BEQ UP DOWN DEX:CPX #$FF:BNE DOK ; underflow ? LDX #0:STX DIR+1 ; Change direction DOK STX NEWPOS+1:RTS UP INX:CPX #$EF:BCC UOK ; overflow ? LDX #$EE:STX DIR+1 ; Change direction UOK STX NEWPOS+1:RTS -------------------------------------------------------------------------- The BFLI file format: File/Memory BFLI Display Rows Offset Offset Rows Size Colors 0-24 0..999 0..999 0-24 1000 I 1000..1023 24 II 0-24 0..975 1024..2000 25.7-50 976 976..999 24 24-24.7 1000..1023 1000..1023 25-25.7 24 Gfx 0-24 0..7999 0..7999 0-24 8000 I 8000..8191 192 II 0-24 0..7807 8192..16000 25.7-50 7808 7808..7999 192 24-24.7 8000..1023 8000..8191 25-25.7 192 The file format has changed three times when I have improved the viewer program, but as fas as I know, this will be the final version. The first version was not at all intuitive (it was a real mess), but this one is even compatible with the normal FLI picture so that you can view BFLI pictures with a FLI viewer and vice versa. You will only see the top half of the BFLI picture with a FLI viewer though. Strangeness only starts after the first half is displayed. VIC-II is at $3e8 when the video bank (and the FLI pic) is switched. So, it just continues to display from there. This is why the beginning of the picture row 25 is inserted at the end of the second FLI picture. When VIC-II reaches the final counter value $3ff, it starts from the beginning again, the end of the picture row 25 and the rest of the picture is in the beginning of the second FLI. Because we only have one color memory and I didn't want to keep copying new data there, both halves use the same color memory. However, because of the wrapping, the color 'memories' of the picture halves 'wrap' on top of eachother, and EVERY location of the color memory is used. You could try to imagine a 50-row color memory with modulo 1024 inserted everywhere. For example the row 26, fourth character would use the color from (26*40+3)%1024=19. --------------------------------------------------------------------------